-
Notifications
You must be signed in to change notification settings - Fork 372
feat(clerk-js): Introduce navigate
for setActive
#6486
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
🦋 Changeset detectedLatest commit: aca655f The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
onPendingStatus
onPendingSession
970cf52
to
cd73364
Compare
c3ad953
to
45a338c
Compare
beb0443
to
1b3771c
Compare
d270d37
to
d185134
Compare
aca655f
to
2de7d6e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🔭 Outside diff range comments (1)
packages/clerk-js/src/core/clerk.ts (1)
1201-1220
: Avoid unsafe cast when resolving session by IDBlindly asserting the found session to SignedInSessionResource can hide bugs and accidentally sign users out when an invalid session ID is passed. Fail fast with a clear error instead.
Apply this diff:
- if (typeof session === 'string') { - session = (this.client.sessions.find(x => x.id === session) as SignedInSessionResource) || null; - } + if (typeof session === 'string') { + const foundSession = this.client.sessions.find(x => x.id === session); + if (!foundSession) { + throw new Error(`setActive: Session with ID "${session}" not found.`); + } + session = foundSession as SignedInSessionResource; + }
♻️ Duplicate comments (15)
packages/clerk-js/src/core/sessionTasks.ts (3)
16-16
: Add explicit return type to getTaskEndpointInternal or not, exported helpers should declare return types for clarity and to avoid accidental widening.
-export const getTaskEndpoint = (task: SessionTask) => `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[task.key]}`; +export const getTaskEndpoint = (task: SessionTask): string => + `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[task.key]}`;
21-31
: Bug: forwarded params placed outside hash; also missing redirectUrl propagation and explicit return typeTask pages are hash-routed. Passing forwarded params via top-level searchParams leaks them outside the hash. Also, the open item to preserve redirectUrl is not implemented, and the function lacks an explicit return type.
Apply all three fixes:
- Use hashSearchParams (not searchParams).
- Accept optional redirectUrl and append it (as redirect_url) to hash params.
- Add explicit return type.
-export function buildTaskUrl(task: SessionTask, opts: Pick<Parameters<typeof buildURL>[0], 'base'>) { - const params = forwardClerkQueryParams(); - - return buildURL( - { - base: opts.base, - hashPath: getTaskEndpoint(task), - searchParams: params, - }, - { stringify: true }, - ); -} +export function buildTaskUrl( + task: SessionTask, + opts: { base: string; redirectUrl?: string }, +): string { + const params = forwardClerkQueryParams(); + if (opts.redirectUrl) { + // keep in the hash to survive SPA hash routing + params.set('redirect_url', opts.redirectUrl); + } + return buildURL( + { + base: opts.base, + hashPath: getTaskEndpoint(task), + hashSearchParams: params, + }, + { stringify: true }, + ) as string; +}
37-53
: Propagate redirectUrl and add explicit return type in navigateIfTaskExistsWhen a session has a pending task, navigation should preserve the original redirectUrl. The function should also declare its return type.
-export function navigateIfTaskExists( - session: SessionResource, - { - navigate, - baseUrl, - }: { - navigate: (to: string) => Promise<unknown>; - baseUrl: string; - }, -) { +export function navigateIfTaskExists( + session: SessionResource, + { + navigate, + baseUrl, + redirectUrl, + }: { + navigate: (to: string) => Promise<unknown>; + baseUrl: string; + redirectUrl?: string; + }, +): Promise<unknown> | void { const currentTask = session.currentTask; if (!currentTask) { return; } - return navigate(buildTaskUrl(currentTask, { base: baseUrl })); + return navigate(buildTaskUrl(currentTask, { base: baseUrl, redirectUrl })); }packages/clerk-js/src/ui/components/SignUp/index.tsx (1)
5-5
: Missing TypeScript path alias configurationThe
@/ui/hooks/usePreloadTasks
import will fail becausetsconfig.json
doesn't define a@/*
path mapping. Add the required path alias configuration or revert to relative imports.-import { usePreloadTasks } from '@/ui/hooks/usePreloadTasks'; +import { usePreloadTasks } from '../../hooks/usePreloadTasks';#!/bin/bash # Verify that TypeScript path mapping for @/ exists fd -t f "tsconfig.*\.json$" -x cat {} \; | jq -r '.compilerOptions.paths' 2>/dev/null | grep -q '"@/\*"' if [ $? -eq 0 ]; then echo "Path mapping for @/ exists" else echo "Path mapping for @/ does NOT exist" fipackages/clerk-js/src/ui/contexts/components/SignIn.ts (2)
32-34
: Update return type and make redirectUrl optional in type definitionThe type definition should make
redirectUrl
optional and usePromise<void>
for clarity since the navigation doesn't return meaningful data.- navigateOnSetActive: (opts: { session: SessionResource; redirectUrl: string }) => Promise<unknown>; + navigateOnSetActive: (opts: { session: SessionResource; redirectUrl?: string }) => Promise<void>;
123-133
: navigateOnSetActive should respect provider-leveltaskUrls
configurationThe function doesn't check for custom task URLs from the
ClerkProvider
configuration. According to the documented precedence rules, provider-leveltaskUrls
should take priority over default task navigation paths.const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => { const currentTask = session.currentTask; if (!currentTask) { return navigate(redirectUrl || afterSignInUrl); } + // Check for custom task URL from provider configuration + const customTaskUrl = clerk.__internal_getOption('taskUrls')?.[currentTask.key]; + if (customTaskUrl) { + return navigate(customTaskUrl); + } + const taskEndpoint = getTaskEndpoint(currentTask); const taskNavigationPath = isCombinedFlow ? '/create' + taskEndpoint : taskEndpoint; return navigate(`/${basePath}${taskNavigationPath}`); };packages/types/src/clerk.ts (3)
123-123
: Add comprehensive JSDoc for SetActiveNavigate public API typeThis public API type needs documentation explaining its purpose, parameters, and usage. Also refine the return type for better type safety.
-export type SetActiveNavigate = ({ session }: { session: SessionResource }) => Promise<unknown>; +/** + * Callback function invoked by `setActive` just before committing the active session/organization. + * Use it to implement custom navigation for pending session tasks. + * + * When provided, this callback takes precedence over `redirectUrl`. + * + * @param ctx.session - The session that is about to become active. It may contain a `currentTask`. + * @returns Optionally return a promise to allow awaiting asynchronous router operations. + * + * @example + * await clerk.setActive({ + * session, + * navigate: async ({ session }) => { + * const task = session.currentTask; + * if (task) { + * await router.push(`/tasks/${task.key}`); + * return; + * } + * await router.push('/dashboard'); + * }, + * }); + */ +export type SetActiveNavigate = (ctx: { session: SessionResource }) => Promise<void> | void;
678-682
: Add JSDoc documentation for buildTasksUrl public API methodThis new public API method needs comprehensive documentation.
+ /** + * Returns the configured URL where session tasks are mounted, based on the current + * session's task and configured taskUrls. + * + * @returns The URL for the current session task, or an empty string if no task exists + * + * @example + * const taskUrl = clerk.buildTasksUrl(); + * // Returns something like: '/sign-in#/tasks/choose-organization' + */ buildTasksUrl(): string;
770-774
: Add JSDoc documentation for redirectToTasks public API methodThis new public API method needs comprehensive documentation.
+ /** + * Redirects to the configured URL where session tasks are mounted. + * Uses the URL generated by `buildTasksUrl()`. + * + * @returns A promise that resolves when navigation is complete + * + * @example + * await clerk.redirectToTasks(); + * // Navigates to the appropriate task URL for the current session + */ redirectToTasks(): Promise<unknown>;packages/clerk-js/src/core/__tests__/clerk.test.ts (4)
447-457
: Type cast mismatch: using PendingSessionResource in active session testThis test is in the "with active session status" suite but casts to PendingSessionResource. Either move the test to the pending session suite or fix the cast to match the test context.
-await sut.setActive({ session: mockSession as any as PendingSessionResource, navigate }); +await sut.setActive({ session: mockSession as any as ActiveSessionResource, navigate });
547-548
: Type cast should be PendingSessionResource, not ActiveSessionResourceThe test verifies behavior for pending sessions but incorrectly casts to ActiveSessionResource.
-await sut.setActive({ session: mockSession as any as ActiveSessionResource }); +await sut.setActive({ session: mockSession as any as PendingSessionResource });
560-560
: Type cast should be PendingSessionResource for consistencySame casting issue - the test is for pending session behavior but uses ActiveSessionResource.
-await sut.setActive({ session: mockSession as any as ActiveSessionResource }); +await sut.setActive({ session: mockSession as any as PendingSessionResource });
564-578
: Add test coverage for navigate callback precedence and redirectUrl preservationThe test suite should verify:
- When both
navigate
callback andtaskUrls
are provided, the callback takes precedenceredirectUrl
passed tosetActive
is preserved in the navigation URLAdd these test cases after the existing "navigate to
taskUrl
option" test:it('prefers navigate callback over taskUrls when both are provided', async () => { mockSession.touch.mockReturnValue(Promise.resolve()); mockClientFetch.mockReturnValue(Promise.resolve({ signedInSessions: [mockSession] })); const navigate = jest.fn(); const sut = new Clerk(productionPublishableKey); sut.navigate = jest.fn(); await sut.load({ taskUrls: { 'choose-organization': '/choose-organization', }, }); await sut.setActive({ session: mockSession as any as PendingSessionResource, navigate }); expect(mockSession.touch).toHaveBeenCalled(); expect(navigate).toHaveBeenCalled(); expect(sut.navigate).not.toHaveBeenCalled(); }); it('preserves redirectUrl when navigating to taskUrl', async () => { mockSession.touch.mockReturnValue(Promise.resolve()); mockClientFetch.mockReturnValue(Promise.resolve({ signedInSessions: [mockSession] })); const sut = new Clerk(productionPublishableKey); sut.navigate = jest.fn(); await sut.load({ taskUrls: { 'choose-organization': '/choose-organization', }, }); await sut.setActive({ session: mockSession as any as PendingSessionResource, redirectUrl: '/dashboard' }); expect(mockSession.touch).toHaveBeenCalled(); // Verify the URL includes the redirect parameter expect(sut.navigate).toHaveBeenCalledWith( expect.stringContaining('redirect_url=/dashboard') ); });packages/clerk-js/src/core/clerk.ts (2)
1851-1873
: Preserve redirectUrl and honor provider overrides during OAuth task navigationCurrent setActiveNavigate drops redirectUrl and ignores provider-level taskUrls. Build the final URL locally and append redirectUrl to hash or query appropriately.
Apply this diff:
const signInUrl = params.signInUrl || displayConfig.signInUrl; const signUpUrl = params.signUpUrl || displayConfig.signUpUrl; - const setActiveNavigate = async ({ - session, - baseUrl, - redirectUrl, - }: { - session: SessionResource; - baseUrl: string; - redirectUrl: string; - }) => { - if (!session.currentTask) { - await this.navigate(redirectUrl); - return; - } - - await navigateIfTaskExists(session, { - baseUrl, - navigate: this.navigate, - }); - }; + const setActiveNavigate = async ({ + session, + baseUrl, + redirectUrl, + }: { + session: SessionResource; + baseUrl: string; + redirectUrl: string; + }) => { + if (!session.currentTask) { + await this.navigate(redirectUrl); + return; + } + const customTaskUrl = this.#options.taskUrls?.[session.currentTask.key]; + let to = + customTaskUrl ?? + (buildURL({ base: baseUrl, hashPath: getTaskEndpoint(session.currentTask) }, { stringify: true }) as string); + if (redirectUrl) { + const useHash = to.includes('#'); + const buildOpts: Record<string, unknown> = { base: to }; + if (useHash) { + buildOpts.hashSearchParams = { redirectUrl }; + } else { + buildOpts.searchParams = { redirectUrl }; + } + to = buildURL(buildOpts, { stringify: true }) as string; + } + await this.navigate(to); + };
1310-1337
: Fix duplicate navigation, preserve redirectUrl correctly, and guard navigate callback errors
- Duplicate navigation: redirectUrl is navigated twice (with auth and then without).
- redirectUrl preservation: always appending as hash params breaks path-based URLs.
- Consumer callback safety: exceptions from setActive.navigate can skip state restoration.
Apply this diff:
if (!beforeEmit && (redirectUrl || taskUrl || setActiveNavigate)) { await tracker.track(async () => { if (!this.client) { // Typescript is not happy because since thinks this.client might have changed to undefined because the function is asynchronous. return; } if (newSession?.status !== 'pending') { this.#setTransitiveState(); } if (taskUrl) { - const taskUrlWithRedirect = redirectUrl - ? buildURL({ base: taskUrl, hashSearchParams: { redirectUrl } }, { stringify: true }) - : taskUrl; - await this.navigate(taskUrlWithRedirect); + let taskUrlWithRedirect = taskUrl; + if (redirectUrl) { + const useHash = taskUrl.includes('#'); + const buildOpts: Record<string, unknown> = { base: taskUrl }; + if (useHash) { + buildOpts.hashSearchParams = { redirectUrl }; + } else { + buildOpts.searchParams = { redirectUrl }; + } + taskUrlWithRedirect = buildURL(buildOpts, { stringify: true }) as string; + } + await this.navigate(taskUrlWithRedirect); } else if (setActiveNavigate && newSession) { - await setActiveNavigate({ session: newSession }); + try { + await setActiveNavigate({ session: newSession }); + } catch (err) { + logger.error('Clerk.setActive navigate callback failed', err); + } } else if (redirectUrl) { - if (this.client.isEligibleForTouch()) { + if (this.client.isEligibleForTouch()) { const absoluteRedirectUrl = new URL(redirectUrl, window.location.href); const redirectUrlWithAuth = this.buildUrlWithAuth( this.client.buildTouchUrl({ redirectUrl: absoluteRedirectUrl }), ); await this.navigate(redirectUrlWithAuth); - } - await this.navigate(redirectUrl); + } else { + await this.navigate(redirectUrl); + } } }); }
🧹 Nitpick comments (9)
packages/clerk-js/src/core/sessionTasks.ts (3)
9-11
: Tighten the mapping type with satisfies (safer than assertion)Avoids unnecessary assertion and ensures future task keys must be added.
-export const INTERNAL_SESSION_TASK_ROUTE_BY_KEY: Record<SessionTask['key'], string> = { - 'choose-organization': 'choose-organization', -} as const; +export const INTERNAL_SESSION_TASK_ROUTE_BY_KEY = { + 'choose-organization': 'choose-organization', +} satisfies Record<SessionTask['key'], string>;
55-69
: Type the function return as void for clarityMinor: make the intent explicit and align with TS best practices for exported helpers.
-export function warnMissingPendingTaskHandlers(options: Record<string, unknown>) { +export function warnMissingPendingTaskHandlers(options: Record<string, unknown>): void { const taskOptions = ['taskUrls', 'navigate'] as Array< keyof (Pick<SetActiveParams, 'navigate'> & Pick<ClerkOptions, 'taskUrls'>) >;
21-31
: Add tests to cover redirectUrl preservation and hash param placementPlease add unit tests that assert:
- buildTaskUrl returns URLs with forwardClerkQueryParams and redirect_url inside the hash, not top-level query.
- navigateIfTaskExists preserves redirectUrl when provided.
Do you want me to scaffold these tests (Jest + URL parsing assertions) for sessionTasks?
Also applies to: 37-53
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx (1)
56-59
: Improve error handling consistencyCatching and directly rejecting loses localized error handling. Use the existing handleError + translateError mechanisms used in other UI components to surface errors consistently.
For example: call handleError(err) and/or pass errors back through VerificationCodeCard controls rather than raw reject().
packages/clerk-js/src/ui/contexts/components/SignIn.ts (1)
123-133
: Consider makingredirectUrl
optional and memoizing the callbackThe
redirectUrl
parameter should be optional since it may not always be provided tosetActive
. Additionally, memoizing the callback would prevent unnecessary re-renders in consuming components.+import { createContext, useContext, useMemo, useCallback } from 'react'; // ... existing imports -const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => { +const navigateOnSetActive = useCallback( + async ({ session, redirectUrl }: { session: SessionResource; redirectUrl?: string }) => { const currentTask = session.currentTask; if (!currentTask) { - return navigate(redirectUrl); + return navigate(redirectUrl || afterSignInUrl); } const taskEndpoint = getTaskEndpoint(currentTask); const taskNavigationPath = isCombinedFlow ? '/create' + taskEndpoint : taskEndpoint; return navigate(`/${basePath}${taskNavigationPath}`); - }; + }, + [navigate, basePath, isCombinedFlow, afterSignInUrl] +);packages/types/src/clerk.ts (1)
1208-1229
: Document the precedence of navigate over redirectUrlThe JSDoc should explicitly mention that when both
navigate
andredirectUrl
are provided,navigate
takes precedence andredirectUrl
is ignored./** * A custom navigation function to be called just before the session and/or organization is set. * - * When provided, it takes precedence over the `redirectUrl` parameter for navigation. + * When provided, it takes precedence over the `redirectUrl` parameter for navigation. + * If both `navigate` and `redirectUrl` are specified, `redirectUrl` will be ignored. * * @example * ```typescript * await clerk.setActive({ * session, + * redirectUrl: '/dashboard', // This will be ignored * navigate: async ({ session }) => { * const currentTask = session.currentTask; * if (currentTask) { * await router.push(`/onboarding/${currentTask.key}`) * return * } * * router.push('/dashboard'); * } * }); * ``` */ navigate?: SetActiveNavigate;packages/clerk-js/src/core/clerk.ts (3)
1554-1574
: Add JSDoc for new public API: buildTasksUrlPublic APIs must be documented. Add behavior, precedence, and a brief example.
Apply this diff:
+ /** + * Builds the URL for the current session task. + * + * Precedence: + * - If a provider-level `taskUrls` override exists for the current task key, that URL is returned. + * - Otherwise, returns a default hash-based tasks URL under the SignIn base. + * + * Returns an empty string when there is no current task. + * + * @public + * @returns {string} + * @example + * const to = clerk.buildTasksUrl(); + * if (to) await clerk.navigate(to); + */ public buildTasksUrl(): string {
1666-1672
: Add JSDoc for new public API: redirectToTasksDocument behavior and that it no-ops outside the browser.
Apply this diff:
- public redirectToTasks = async (): Promise<unknown> => { + /** + * Navigates to the current session task URL, if any. + * No-ops on non-browser environments. + * + * @public + * @returns {Promise<unknown>} + */ + public redirectToTasks = async (): Promise<unknown> => {
1201-1356
: Tests needed for new after-auth flows and edge casesAdd/extend tests to cover:
- setActive().navigate precedence vs taskUrls
- redirectUrl propagation for both hash and path routes
- error handling when navigate callback throws
- SSO/Web3 flows navigating to tasks with provider overrides
Do you want me to draft unit/integration tests for these cases (including hash vs query preservation)? I can propose Jest/Vitest test scaffolding aligned with existing suites.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap
is excluded by!**/*.snap
packages/remix/src/__tests__/__snapshots__/exports.test.ts.snap
is excluded by!**/*.snap
packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap
is excluded by!**/*.snap
📒 Files selected for processing (50)
.changeset/rich-donuts-agree.md
(1 hunks).changeset/warm-rocks-flow.md
(1 hunks)integration/tests/session-tasks-sign-in.test.ts
(1 hunks)integration/tests/session-tasks-sign-up.test.ts
(4 hunks)packages/clerk-js/bundlewatch.config.json
(1 hunks)packages/clerk-js/src/core/__tests__/clerk.test.ts
(9 hunks)packages/clerk-js/src/core/clerk.ts
(21 hunks)packages/clerk-js/src/core/resources/SignIn.ts
(1 hunks)packages/clerk-js/src/core/resources/SignUp.ts
(1 hunks)packages/clerk-js/src/core/sessionTasks.ts
(1 hunks)packages/clerk-js/src/ui/common/redirects.ts
(0 hunks)packages/clerk-js/src/ui/common/withRedirect.tsx
(3 hunks)packages/clerk-js/src/ui/components/SessionTasks/index.tsx
(5 hunks)packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/ChooseOrganizationScreen.tsx
(3 hunks)packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx
(3 hunks)packages/clerk-js/src/ui/components/SessionTasks/tasks/withTaskGuard.ts
(1 hunks)packages/clerk-js/src/ui/components/SignIn/SignInAccountSwitcher.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativeChannelCodeForm.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/SignInFactorOneCodeForm.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/SignInFactorTwo.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoCodeForm.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/SignInSSOCallback.tsx
(1 hunks)packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
(7 hunks)packages/clerk-js/src/ui/components/SignIn/__tests__/handleCombinedFlowTransfer.test.ts
(5 hunks)packages/clerk-js/src/ui/components/SignIn/handleCombinedFlowTransfer.ts
(4 hunks)packages/clerk-js/src/ui/components/SignIn/index.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignIn/shared.ts
(2 hunks)packages/clerk-js/src/ui/components/SignUp/SignUpContinue.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignUp/SignUpEmailLinkCard.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignUp/SignUpSSOCallback.tsx
(1 hunks)packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx
(5 hunks)packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
(2 hunks)packages/clerk-js/src/ui/components/SignUp/index.tsx
(1 hunks)packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx
(3 hunks)packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx
(2 hunks)packages/clerk-js/src/ui/contexts/components/SignIn.ts
(4 hunks)packages/clerk-js/src/ui/contexts/components/SignUp.ts
(4 hunks)packages/clerk-js/src/ui/lazyModules/components.ts
(1 hunks)packages/clerk-js/src/ui/types.ts
(2 hunks)packages/clerk-js/src/utils/componentGuards.ts
(1 hunks)packages/nextjs/src/client-boundary/controlComponents.ts
(1 hunks)packages/nextjs/src/index.ts
(0 hunks)packages/react/src/components/controlComponents.tsx
(1 hunks)packages/react/src/components/index.ts
(1 hunks)packages/react/src/isomorphicClerk.ts
(2 hunks)packages/types/src/clerk.ts
(5 hunks)packages/vue/src/components/controlComponents.ts
(1 hunks)
💤 Files with no reviewable changes (2)
- packages/nextjs/src/index.ts
- packages/clerk-js/src/ui/common/redirects.ts
🚧 Files skipped from review as they are similar to previous changes (37)
- integration/tests/session-tasks-sign-in.test.ts
- packages/nextjs/src/client-boundary/controlComponents.ts
- packages/clerk-js/src/ui/components/SignIn/SignInSSOCallback.tsx
- packages/clerk-js/src/ui/components/SignUp/SignUpEmailLinkCard.tsx
- packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx
- packages/clerk-js/src/ui/components/SignIn/SignInFactorOneCodeForm.tsx
- packages/clerk-js/src/ui/components/SignIn/SignInFactorTwo.tsx
- .changeset/rich-donuts-agree.md
- packages/clerk-js/src/ui/components/SignIn/tests/handleCombinedFlowTransfer.test.ts
- packages/clerk-js/src/ui/components/SignIn/shared.ts
- packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx
- packages/clerk-js/src/ui/types.ts
- packages/clerk-js/src/ui/lazyModules/components.ts
- packages/react/src/components/controlComponents.tsx
- packages/clerk-js/src/ui/components/SignIn/SignInAccountSwitcher.tsx
- packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativeChannelCodeForm.tsx
- .changeset/warm-rocks-flow.md
- packages/clerk-js/src/utils/componentGuards.ts
- packages/react/src/components/index.ts
- packages/clerk-js/src/ui/components/SignUp/SignUpSSOCallback.tsx
- packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx
- packages/clerk-js/src/ui/components/SignUp/SignUpContinue.tsx
- packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoCodeForm.tsx
- packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/ChooseOrganizationScreen.tsx
- packages/vue/src/components/controlComponents.ts
- integration/tests/session-tasks-sign-up.test.ts
- packages/clerk-js/src/ui/contexts/components/SignUp.ts
- packages/clerk-js/src/ui/components/SessionTasks/tasks/withTaskGuard.ts
- packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
- packages/clerk-js/src/ui/components/SessionTasks/index.tsx
- packages/clerk-js/src/core/resources/SignIn.ts
- packages/clerk-js/src/ui/common/withRedirect.tsx
- packages/clerk-js/bundlewatch.config.json
- packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx
- packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx
- packages/clerk-js/src/ui/components/SignIn/handleCombinedFlowTransfer.ts
- packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}
: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/clerk-js/src/core/resources/SignUp.ts
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/clerk-js/src/core/clerk.ts
packages/clerk-js/src/core/sessionTasks.ts
packages/types/src/clerk.ts
packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/react/src/isomorphicClerk.ts
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/clerk-js/src/core/resources/SignUp.ts
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/clerk-js/src/core/clerk.ts
packages/clerk-js/src/core/sessionTasks.ts
packages/types/src/clerk.ts
packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/react/src/isomorphicClerk.ts
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
packages/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/core/resources/SignUp.ts
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/clerk-js/src/core/clerk.ts
packages/clerk-js/src/core/sessionTasks.ts
packages/types/src/clerk.ts
packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/react/src/isomorphicClerk.ts
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/clerk-js/src/core/resources/SignUp.ts
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/clerk-js/src/core/clerk.ts
packages/clerk-js/src/core/sessionTasks.ts
packages/types/src/clerk.ts
packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/react/src/isomorphicClerk.ts
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}
: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertions
for literal types:as const
Usesatisfies
operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/clerk-js/src/core/resources/SignUp.ts
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/clerk-js/src/core/clerk.ts
packages/clerk-js/src/core/sessionTasks.ts
packages/types/src/clerk.ts
packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/react/src/isomorphicClerk.ts
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/clerk-js/src/core/resources/SignUp.ts
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/clerk-js/src/core/clerk.ts
packages/clerk-js/src/core/sessionTasks.ts
packages/types/src/clerk.ts
packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/react/src/isomorphicClerk.ts
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
**/*
⚙️ CodeRabbit Configuration File
If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.
Files:
packages/clerk-js/src/core/resources/SignUp.ts
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/clerk-js/src/core/clerk.ts
packages/clerk-js/src/core/sessionTasks.ts
packages/types/src/clerk.ts
packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/react/src/isomorphicClerk.ts
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
packages/clerk-js/src/ui/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/clerk-js-ui.mdc)
packages/clerk-js/src/ui/**/*.{ts,tsx}
: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure
Files:
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/contexts/components/SignIn.ts
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
**/*.{jsx,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{jsx,tsx}
: Use error boundaries in React components
Minimize re-renders in React components
**/*.{jsx,tsx}
: Always use functional components with hooks instead of class components
Follow PascalCase naming for components:UserProfile
,NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...
Files:
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/react.mdc)
**/*.tsx
: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering
Files:
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx
packages/clerk-js/src/ui/components/SignIn/index.tsx
packages/clerk-js/src/ui/components/SignUp/index.tsx
packages/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Unit tests should use Jest or Vitest as the test runner.
Files:
packages/clerk-js/src/core/__tests__/clerk.test.ts
packages/{clerk-js,elements,themes}/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Visual regression testing should be performed for UI components.
Files:
packages/clerk-js/src/core/__tests__/clerk.test.ts
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/typescript.mdc)
**/__tests__/**/*.{ts,tsx}
: Create type-safe test builders/factories
Use branded types for test isolation
Implement proper mock types that match interfaces
Files:
packages/clerk-js/src/core/__tests__/clerk.test.ts
🧠 Learnings (1)
📚 Learning: 2025-08-08T19:00:08.988Z
Learnt from: LauraBeatris
PR: clerk/javascript#6486
File: packages/clerk-js/src/core/clerk.ts:1303-1344
Timestamp: 2025-08-08T19:00:08.988Z
Learning: In Clerk's navigation system for pending sessions, `taskUrls` (configured at the ClerkProvider level) should take priority over the `navigate` callback (from All-In-One components). This ensures that explicit provider-level configuration overrides component-level navigation logic, providing developers with predictable control over navigation behavior.
Applied to files:
packages/clerk-js/src/core/clerk.ts
🧬 Code Graph Analysis (6)
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx (1)
packages/clerk-js/src/ui/contexts/components/SignIn.ts (1)
useSignInContext
(38-169)
packages/clerk-js/src/ui/contexts/components/SignIn.ts (7)
packages/types/src/session.ts (1)
SessionResource
(208-261)packages/clerk-js/src/ui/types.ts (1)
SignInCtx
(64-68)packages/clerk-js/src/ui/router/RouteContext.tsx (1)
useRouter
(33-39)packages/clerk-js/src/core/resources/Session.ts (1)
currentTask
(390-393)packages/react/src/isomorphicClerk.ts (1)
session
(672-678)packages/clerk-js/src/core/sessionTasks.ts (1)
getTaskEndpoint
(16-16)packages/clerk-js/src/ui/common/redirects.ts (1)
buildRedirectUrl
(58-79)
packages/clerk-js/src/core/clerk.ts (8)
packages/clerk-js/src/utils/componentGuards.ts (1)
isSignedInAndSingleSessionModeEnabled
(9-11)packages/types/src/clerk.ts (1)
SetActiveParams
(1185-1230)packages/react/src/isomorphicClerk.ts (1)
session
(672-678)packages/types/src/session.ts (2)
SignedInSessionResource
(285-285)SessionResource
(208-261)packages/clerk-js/src/core/sessionTasks.ts (3)
warnMissingPendingTaskHandlers
(55-69)getTaskEndpoint
(16-16)navigateIfTaskExists
(37-53)packages/clerk-js/src/utils/url.ts (1)
buildURL
(84-158)packages/clerk-js/src/core/resources/Session.ts (1)
currentTask
(390-393)packages/clerk-js/src/utils/runtime.ts (1)
inBrowser
(1-3)
packages/clerk-js/src/core/sessionTasks.ts (4)
packages/types/src/session.ts (1)
SessionTask
(332-337)packages/clerk-js/src/utils/url.ts (1)
buildURL
(84-158)packages/clerk-js/src/utils/getClerkQueryParam.ts (1)
forwardClerkQueryParams
(58-70)packages/clerk-js/src/core/resources/Session.ts (1)
currentTask
(390-393)
packages/types/src/clerk.ts (1)
packages/types/src/session.ts (1)
SessionResource
(208-261)
packages/clerk-js/src/core/__tests__/clerk.test.ts (3)
packages/types/src/clerk.ts (1)
Clerk
(164-886)packages/clerk-js/src/core/clerk.ts (1)
Clerk
(193-2878)packages/types/src/session.ts (1)
PendingSessionResource
(275-279)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
- GitHub Check: Integration Tests (tanstack-react-router, chrome)
- GitHub Check: Integration Tests (nextjs, chrome, 15)
- GitHub Check: Integration Tests (vue, chrome)
- GitHub Check: Integration Tests (nuxt, chrome)
- GitHub Check: Integration Tests (billing, chrome)
- GitHub Check: Integration Tests (react-router, chrome)
- GitHub Check: Integration Tests (machine, chrome)
- GitHub Check: Integration Tests (elements, chrome)
- GitHub Check: Integration Tests (nextjs, chrome, 14)
- GitHub Check: Integration Tests (expo-web, chrome)
- GitHub Check: Integration Tests (express, chrome)
- GitHub Check: Integration Tests (sessions, chrome)
- GitHub Check: Integration Tests (ap-flows, chrome)
- GitHub Check: Integration Tests (tanstack-react-start, chrome)
- GitHub Check: Integration Tests (astro, chrome)
- GitHub Check: Integration Tests (quickstart, chrome)
- GitHub Check: Integration Tests (localhost, chrome)
- GitHub Check: Integration Tests (generic, chrome)
- GitHub Check: Unit Tests (18, --filter=@clerk/astro --filter=@clerk/backend --filter=@clerk/express --filter=@c...
- GitHub Check: Publish with pkg-pr-new
- GitHub Check: Unit Tests (22, **)
- GitHub Check: Static analysis
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (16)
packages/clerk-js/src/ui/components/SignIn/index.tsx (2)
138-140
: LGTM: tasks route exposure under combined flowAdding the create/tasks route aligns with the new task navigation. Ensure LazySessionTasks handles nested task paths under /tasks/*.
If your router requires wildcards for nested segments (e.g., /tasks/choose-organization), confirm the route matches them. If necessary, adjust:
- Route path='tasks/*'
17-17
: Router import change looks fineNo issues with the router imports; consistent with current usage across the file.
packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx (1)
27-27
: Good adoption of navigate-on-setActiveSwitching to navigateOnSetActive with afterSignInUrl preserves the new API contract and avoids automatic redirects.
packages/clerk-js/src/ui/components/SignUp/SignUpVerificationCodeForm.tsx (1)
27-27
: Good: delegate navigation via navigateOnSetActiveThis aligns with the new setActive navigate callback and centralizes route logic in context.
packages/clerk-js/src/core/resources/SignUp.ts (1)
297-297
: Behavior change: actionCompleteRedirectUrl now always redirectUrlCompleteThis removes the prior conditional behavior. Verify:
- SSO callback flows still route to the intended task or completion URLs.
- Any pending-task navigation after SSO continues to work (the PR notes issues here).
Recommend targeted E2E verification for:
- Social SSO sign-up with a pending task (e.g., choose-organization): confirm redirect lands on the task, and redirectUrl is preserved when completing the task.
- Non-pending flows: confirm redirectUrlComplete remains honored.
If issues persist, consider deferring to the new central task-navigation helpers in sessionTasks and contexts for consistency.
packages/clerk-js/src/ui/contexts/components/SignIn.ts (2)
123-133
: Remove debug statement and unused variableThe
debugger
statement on line 125 violates ESLint'sno-debugger
rule and will break production builds. Additionally,taskNavigationPath
is computed but never used, causing an ESLintno-unused-vars
violation.const navigateOnSetActive = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => { - debugger; const currentTask = session.currentTask; if (!currentTask) { return navigate(redirectUrl); } const taskEndpoint = getTaskEndpoint(currentTask); - const taskNavigationPath = isCombinedFlow ? '/create' + taskEndpoint : taskEndpoint; + const navigationPath = (isCombinedFlow ? '/create' : '') + taskEndpoint; - return navigate(`/${basePath + path}`); + return navigate(`/${basePath}${navigationPath}`); };Likely an incorrect or invalid review comment.
132-132
:path
is undefined - will cause runtime errorThe variable
path
is not defined in this scope, which will cause a runtime error when navigating to a task URL. The code should use the computedtaskNavigationPath
(ornavigationPath
from the fix above).- return navigate(`/${basePath + path}`); + return navigate(`/${basePath}${taskNavigationPath}`);Likely an incorrect or invalid review comment.
packages/react/src/isomorphicClerk.ts (2)
391-398
: LGTM! Clean implementation of buildTasksUrlThe implementation correctly follows the established pre-mount queuing pattern used by other build methods, maintaining consistency across the codebase.
1279-1287
: LGTM! Consistent implementation of redirectToTasksThe async redirect method correctly follows the established pattern, properly handling both loaded and pre-mount states.
packages/clerk-js/src/core/__tests__/clerk.test.ts (1)
1036-1036
: LGTM! Test correctly reflects new task navigation behaviorThe expectation properly validates navigation to the task URL using the new navigation pattern.
packages/clerk-js/src/core/clerk.ts (6)
554-561
: LGTM: Guarding SignIn in single-session mode is correctBlocking SignIn when already signed-in and single-session mode is enabled matches expected UX, and the dev-only error helps during integration.
689-695
: LGTM: Guarding SignUp in single-session mode is correctSame rationale as SignIn guard; behavior and dev-only error are appropriate.
1231-1234
: Good fix: no more false positives in pending-task warningPassing the merged source (
{ ...this.#options, ...params }
) ensures warnMissingPendingTaskHandlers can see taskUrls/navigate.
1266-1271
: LGTM: Pending-session cache revalidation guardSkipping onBeforeSetActive for pending sessions avoids unnecessary unmounts during task navigation.
1305-1309
: LGTM: Compute taskUrl from the new sessionUsing newSession.currentTask respects the session being activated and aligns with provider-level overrides precedence.
2002-2005
: LGTM: Centralized fallback to redirectToTasksUsing the new public API here keeps SSO callback logic simple and consistent with task-url precedence.
const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => { | ||
if (!session.currentTask) { | ||
await this.navigate(redirectUrl); | ||
return; | ||
} | ||
|
||
await navigateIfTaskExists(session, { | ||
baseUrl: displayConfig.signInUrl, | ||
navigate: this.navigate, | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Web3 after-auth parity with OAuth: preserve redirectUrl and provider overrides
Mirror OAuth behavior: build the task URL locally, append redirectUrl properly, and respect taskUrls.
Apply this diff:
- const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
- if (!session.currentTask) {
- await this.navigate(redirectUrl);
- return;
- }
-
- await navigateIfTaskExists(session, {
- baseUrl: displayConfig.signInUrl,
- navigate: this.navigate,
- });
- };
+ const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => {
+ if (!session.currentTask) {
+ await this.navigate(redirectUrl);
+ return;
+ }
+ const customTaskUrl = this.#options.taskUrls?.[session.currentTask.key];
+ let to =
+ customTaskUrl ??
+ (buildURL({ base: displayConfig.signInUrl, hashPath: getTaskEndpoint(session.currentTask) }, { stringify: true }) as string);
+ if (redirectUrl) {
+ const useHash = to.includes('#');
+ const buildOpts: Record<string, unknown> = { base: to };
+ if (useHash) {
+ buildOpts.hashSearchParams = { redirectUrl };
+ } else {
+ buildOpts.searchParams = { redirectUrl };
+ }
+ to = buildURL(buildOpts, { stringify: true }) as string;
+ }
+ await this.navigate(to);
+ };
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => { | |
if (!session.currentTask) { | |
await this.navigate(redirectUrl); | |
return; | |
} | |
await navigateIfTaskExists(session, { | |
baseUrl: displayConfig.signInUrl, | |
navigate: this.navigate, | |
}); | |
}; | |
const setActiveNavigate = async ({ session, redirectUrl }: { session: SessionResource; redirectUrl: string }) => { | |
if (!session.currentTask) { | |
await this.navigate(redirectUrl); | |
return; | |
} | |
const customTaskUrl = this.#options.taskUrls?.[session.currentTask.key]; | |
let to = | |
customTaskUrl ?? | |
(buildURL( | |
{ base: displayConfig.signInUrl, hashPath: getTaskEndpoint(session.currentTask) }, | |
{ stringify: true } | |
) as string); | |
if (redirectUrl) { | |
const useHash = to.includes('#'); | |
const buildOpts: Record<string, unknown> = { base: to }; | |
if (useHash) { | |
buildOpts.hashSearchParams = { redirectUrl }; | |
} else { | |
buildOpts.searchParams = { redirectUrl }; | |
} | |
to = buildURL(buildOpts, { stringify: true }) as string; | |
} | |
await this.navigate(to); | |
}; |
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/clerk.ts around lines 2175 to 2185, the
setActiveNavigate implementation for Web3 diverges from OAuth: instead of
calling navigateIfTaskExists blindly, build the task URL locally using
session.currentTask.taskUrl or displayConfig.taskUrls/provider overrides, append
the redirectUrl as a query param (properly encoding), and then call
this.navigate with that constructed URL; ensure provider-specific overrides are
honored and that fallback to displayConfig.signInUrl occurs when taskUrl is
missing.
return setActive({ | ||
session: res.createdSessionId, | ||
navigate: async ({ session }) => { | ||
await navigateOnSetActive({ session, redirectUrl: afterSignInUrl }); | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify navigateOnSetActive respects taskUrls overrides and preserves params
The SignIn context’s navigateOnSetActive (see packages/clerk-js/src/ui/contexts/components/SignIn.ts) builds a task path from INTERNAL_SESSION_TASK_ROUTE_BY_KEY and does not appear to use taskUrls or forward preserved params. That would bypass user-provided overrides and drop important query params.
Run this script to confirm current behavior and surface any missed wiring:
If confirmed, consider:
- Respecting taskUrls in navigateOnSetActive when present.
- Forwarding preserved params (e.g., via forwardClerkQueryParams) into the hash for SPA routes.
---
🏁 Script executed:
```shell
#!/bin/bash
# Inspect navigateOnSetActive implementations and taskUrls usage
rg -n "navigateOnSetActive" -A 40 packages/clerk-js/src/ui/contexts/components
echo "----"
rg -n "taskUrls" -A 5 packages/clerk-js/src/ui/contexts/components
echo "----"
rg -n "getTaskEndpoint|INTERNAL_SESSION_TASK_ROUTE_BY_KEY" -A 5 packages/clerk-js/src/ui/contexts/components
Length of output: 20238
Ensure navigateOnSetActive
respects custom taskUrls and retains query parameters
Currently in packages/clerk-js/src/ui/contexts/components/SignIn.ts (lines 123–133), navigateOnSetActive
ignores any user-provided taskUrls
overrides and drops preserved query params when session.currentTask
exists. Update it to:
• Check for an override via clerk.__internal_getOption('taskUrls')?.[currentTask.key]
• If present, call navigate
with that URL (forwarding queryParams
)
• Otherwise build the default path as before, but still wrap it with forwardClerkQueryParams
Locations to update:
- packages/clerk-js/src/ui/contexts/components/SignIn.ts, function
navigateOnSetActive
(around line 123)
Suggested diff:
@@ packages/clerk-js/src/ui/contexts/components/SignIn.ts
const navigateOnSetActive = async ({ session, redirectUrl }) => {
const currentTask = session.currentTask;
if (!currentTask) {
return navigate(redirectUrl);
}
- const taskEndpoint = getTaskEndpoint(currentTask);
- const taskNavigationPath = isCombinedFlow ? '/create' + taskEndpoint : taskEndpoint;
- return navigate(`/${basePath + taskNavigationPath}`);
+ // Prefer user-provided override
+ const overrideUrl = clerk.__internal_getOption('taskUrls')?.[currentTask.key];
+ if (overrideUrl) {
+ return navigate(forwardClerkQueryParams(overrideUrl, queryParams));
+ }
+
+ // Fallback to default task route, preserving query params
+ const taskEndpoint = getTaskEndpoint(currentTask);
+ const taskPath = isCombinedFlow ? '/create' + taskEndpoint : taskEndpoint;
+ const defaultUrl = `/${basePath + taskPath}`;
+ return navigate(forwardClerkQueryParams(defaultUrl, queryParams));
};
Be sure to import and use forwardClerkQueryParams
and pull in clerk
and queryParams
from the outer scope.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/contexts/components/SignIn.ts around lines 123 to
133, navigateOnSetActive currently ignores user-provided taskUrls overrides and
drops preserved query params when session.currentTask exists; update it to first
check clerk.__internal_getOption('taskUrls')?.[currentTask.key] and if present
call navigate with that override URL while forwarding queryParams (use
forwardClerkQueryParams to attach them), otherwise build the default path as
before but still wrap it with forwardClerkQueryParams; ensure clerk and
queryParams are referenced from the outer scope and import/use
forwardClerkQueryParams in this file.
handleComplete: () => | ||
setActive({ | ||
session: res.createdSessionId, | ||
navigate: async ({ session }) => { | ||
await navigateOnSetActive({ session, redirectUrl: afterSignUpUrl }); | ||
}, | ||
}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Ensure navigateOnSetActive honors taskUrls and preserves redirectUrl/query params
The SignUp context’s navigateOnSetActive (see packages/clerk-js/src/ui/contexts/components/SignUp.ts) currently constructs a static /tasks/... path; it may ignore configured taskUrls and drop preserved params. This could regress the PR goal to support ClerkProvider.taskUrls + redirectUrl preservation.
Use this to verify:
If confirmed, update navigateOnSetActive to:
- Prefer options.taskUrls[currentTask.key] when provided (append redirect_url appropriately).
- Otherwise build a URL with getTaskEndpoint and forwardClerkQueryParams + redirect_url into hashSearchParams.
---
🏁 Script executed:
```shell
#!/bin/bash
rg -n "navigateOnSetActive" -A 40 packages/clerk-js/src/ui/contexts/components/SignUp.ts
rg -n "taskUrls" -A 5 packages/clerk-js/src/ui/contexts/components/SignUp.ts
Length of output: 3237
Fix navigateOnSetActive to respect taskUrls and preserve redirectUrl/query params
The current implementation in
packages/clerk-js/src/ui/contexts/components/SignUp.ts (lines 118–125) always does:
if (!currentTask) {
return navigate(redirectUrl);
}
return navigate(`/${basePath}/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTask.key]}`);
This ignores any custom options.taskUrls
and drops both the incoming redirectUrl
and existing Clerk query parameters. To fully support ClerkProvider.taskUrls
and URL-param preservation, update navigateOnSetActive
to:
- Prefer
options.taskUrls[currentTask.key]
, appendingredirect_url
plus the existing Clerk query params (fromqueryParams
). - Otherwise fall back to your standard
buildRedirectUrl({ routing, baseUrl: signInUrl, authQueryString, path, endpoint: getTaskEndpoint(currentTask), redirectUrl })
. - Call
navigate()
with that computed URL instead of the hard-coded/tasks/...
route.
For example:
- return navigate(`/${basePath}/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTask.key]}`);
+ const taskUrls = clerk.__internal_getOption('taskUrls');
+ const customTaskUrl = taskUrls?.[currentTask.key];
+ const targetUrl = customTaskUrl
+ ? appendSearchParams(customTaskUrl, { redirect_url: redirectUrl, ...queryParams })
+ : buildRedirectUrl({
+ routing: ctx.routing,
+ baseUrl: signInUrl,
+ authQueryString,
+ path: ctx.path,
+ endpoint: getTaskEndpoint(currentTask),
+ redirectUrl,
+ });
+ return navigate(targetUrl);
This ensures both custom task endpoints and all query params (including your post-signup redirect) are honored.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/contexts/components/SignUp.ts around lines 118–125,
navigateOnSetActive currently ignores options.taskUrls and drops redirectUrl and
existing Clerk query params; update it to compute a targetUrl: if currentTask
exists and options.taskUrls[currentTask.key] is defined use that URL as the base
and append redirect_url (redirectUrl) plus the existing Clerk query params from
queryParams; otherwise call buildRedirectUrl({ routing, baseUrl: signInUrl,
authQueryString, path, endpoint: getTaskEndpoint(currentTask), redirectUrl }) to
compute the fallback URL; finally call navigate(targetUrl) with the computed URL
(not the hard-coded /tasks/ route) so custom taskUrls and all query params are
preserved.
Description
This PR introduces a new API to navigate on
setActive
, that can be leveraged for after-auth flows to handle pending sessions, featuring a callback mechanism that enables developers to integrate their application logic, such as navigation.Previously, our flows were mistakenly navigating after
setActive
, which could cause issues with session revalidation and re-render of pages.From now on, we're proposing two options to deal with after-auth:
taskUrls
ornavigate
navigate
A callback function that can be provided as an
setActive
option.taskUrls
(existing)A map of URLs per session task key that can be provided as a Clerk option. On
setActive
, it'll be used for navigation.AIOs behavior
CleanShot.2025-08-07.at.22.30.57.mp4
Checklist
pnpm test
runs as expected.pnpm build
runs as expected.Type of change
Summary by CodeRabbit
New Features
Refactor
Tests
Chores